home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 007 / as03.arc / PROFIL.ASM < prev    next >
Assembly Source File  |  1985-08-06  |  21KB  |  705 lines

  1.         TITLE   PROFIL - MS-DOS Profile program
  2.  
  3. ;Profiler for MS-DOS 1.25 2.00
  4. ;
  5. ; Lots of stuff stolen from debug.
  6. ; User provides # of paragraphs per bucket, program is cut up accordingly.
  7. ; User also specifies clock interval
  8.  
  9.  
  10. ;System calls
  11. PRINTBUF        EQU     9
  12. SETDMA          EQU     26
  13. CREATE          EQU     22
  14. OPEN            EQU     15
  15. CLOSE           EQU     16
  16. GETBUF          EQU     10
  17. BLKWRT          EQU     40
  18. BLKRD           EQU     39
  19. OUTCH           EQU     2
  20. SETBASE         EQU     38
  21.  
  22. FCB             EQU     5CH
  23. BUFLEN          EQU     80
  24.  
  25. ; FCB offsets
  26. RR              EQU     33
  27. RECLEN          EQU     14
  28. FILELEN         EQU     16
  29.  
  30.  
  31. ;Segments in load order
  32.  
  33. CODE    SEGMENT PUBLIC
  34. CODE    ENDS
  35.  
  36. DATA    SEGMENT BYTE
  37. DATA    ENDS
  38.  
  39. INIT    SEGMENT BYTE
  40. INIT    ENDS
  41.  
  42. DG      GROUP   CODE,DATA,INIT
  43.  
  44. ;The data segment
  45.  
  46. DATA    SEGMENT BYTE
  47.         ORG     0
  48. ENDMES          DB      13,10,"Program terminated normally",13,10,"$"
  49. ABORTMES        DB      13,10,"Program aborted",13,10,"$"
  50. TOOBIG          DB      "Program too big",13,10,"$"
  51. EXEBAD          DB      "EXE file bad",13,10,"$"
  52.  
  53. OUT_FCB         LABEL   WORD
  54.                 DB      0
  55. OUTNAME         DB      "        PRF"
  56.                 DB      30 DUP(0)
  57.  
  58.                 DB      80H DUP(?)
  59. STACK           LABEL   WORD
  60.  
  61. BYTEBUF         DB      BUFLEN DUP(?)           ;Processed input queue
  62. AXSAVE          DW      ?                       ;See interrupt routine
  63. BXSAVE          DW      ?                       ; "     "        "
  64. PROG_AREA       DW      ?                       ;Segment of program start
  65.  
  66. ;EXE file header
  67. RUNVAR          LABEL   WORD
  68. RELPT           DW      ?
  69. LASTP           LABEL   WORD
  70. RELSEG          DW      ?
  71. PSIZE           LABEL   WORD
  72. PAGES           DW      ?
  73. RELCNT          DW      ?
  74. HEADSIZ         DW      ?
  75.                 DW      ?
  76. LOADLOW         DW      ?
  77. PROG_SS         LABEL   WORD                    ;Program stack seg
  78. INITSS          DW      ?
  79. PROG_SP         LABEL   WORD                    ;Program SP
  80. INITSP          DW      ?
  81.                 DW      ?
  82. PROG_ENTRY      EQU     THIS DWORD
  83. PROG_RA         LABEL   WORD                    ;Program start offset
  84. INITIP          DW      ?
  85. PROG_SA         LABEL   WORD                    ;Program start segment (may be different from PROG_AREA)
  86. INITCS          DW      ?
  87. RELTAB          DW      ?
  88. RUNVARSIZ       EQU     $-RUNVAR
  89.  
  90. EXEFILE         DB      0                       ;Flag to indicate EXE file
  91. DRV_VALID       DW      ?                       ;Init for AX register
  92. OUTPUT_DATA     LABEL   WORD                    ;Start of the profile data
  93. CLOCK_GRAIN     DW      ?                       ;Clock interval micro-seconds
  94. BUCKET_NUM      DW      ?                       ;Number of buckets
  95. BUCKET_SIZE     DW      ?                       ;Paragraphs per bucket
  96. PROG_LOW_PA     DW      ?                       ;Start of program (PARA #)
  97. PROG_HIGH_PA    DW      ?                       ;End of program (PARA #)
  98. DOS_PA          DW      ?                       ;IO-DOS PARA boundry
  99. HIT_IO          DW      0                       ;IO bucket
  100. HIT_DOS         DW      0                       ;DOS bucket
  101. HIT_HIGH        DW      0                       ;Above Program bucket
  102. NUM_DATA_WORDS  EQU     ($-OUTPUT_DATA)/2       ;Number of word items
  103. BUCKET          LABEL   WORD                    ;Bucket count area
  104.  
  105. ;The following data will be overwritten when the buckets are initialized
  106. LINEBUF         DB      BUFLEN,1,0DH            ;Raw input buffer
  107.                 DB      BUFLEN DUP(?)
  108.  
  109. NOFILE          DB      "File not found",13,10,"$"
  110. OUTERR          DB      "Cannot open output file",13,10,"$"
  111. GRAIN_PROMPT    DB      "Sample time (micro-sec) >= 60 ? ","$"
  112. SIZE_PROMPT     DB      "Number of paragraphs (16 bytes) per bucket? ","$"
  113. PARAM_PROMPT    DB      "Parameters to program? ","$"
  114. DATA    ENDS
  115.  
  116. ;The resident code portion
  117. CODE    SEGMENT PUBLIC
  118. ASSUME  CS:DG,DS:DG,ES:DG,SS:DG
  119.  
  120. ;The clock interrupt routine
  121.         PUBLIC  CLK_INTER
  122.  
  123. ;Stuff provided by external clock handler routine
  124.         EXTRN   CLOCKON:NEAR,CLOCKOFF:NEAR,LEAVE_INT:NEAR
  125.  
  126.         ORG     100H
  127. START:
  128.         CLD
  129.         MOV     SP,OFFSET DG:STACK      ;Use internal stack
  130.         CALL    SETUP
  131. ;The following setup stuff cannot be done in SETUP because we're probably
  132. ; overwritting the INIT area
  133.         MOV     DX,[PROG_AREA]
  134.         MOV     AH,SETBASE
  135.         INT     21H                     ;Set base for program
  136.         MOV     ES,[PROG_AREA]
  137.         PUSH    SI                      ;Points to BYTEBUF
  138.         MOV     DI,81H                  ;Set unformatted params
  139. COMTAIL:
  140.         LODSB
  141.         STOSB
  142.         CMP     AL,13
  143.         JNZ     COMTAIL
  144.         SUB     DI,82H                  ;Figure length
  145.         XCHG    AX,DI
  146.         MOV     BYTE PTR ES:[80H],AL
  147.         POP     SI
  148.         MOV     DI,FCB                  ;First param
  149.         MOV     AX,2901H
  150.         INT     21H
  151.         MOV     BYTE PTR [DRV_VALID],AL
  152.         MOV     AX,2901H                
  153.         MOV     DI,6CH                  ;Second param
  154.         INT     21H
  155.         MOV     BYTE PTR [DRV_VALID+1],AL
  156.  
  157.         MOV     AX,ES                   ;Prog segment to AX
  158.         MOV     DX,[PROG_RA]            ;Offset
  159.         CMP     [EXEFILE],1
  160.         JZ      EXELOAD                 ;EXE file
  161.         JMP     BINFIL                  ;Regular file (.COM)
  162.  
  163. EXELOAD:
  164.         MOV     AX,[HEADSIZ]            ;Size of header in paragraphs
  165.         ADD     AX,31
  166.         MOV     CL,4
  167.         ROL     AX,CL                   ;Size in bytes
  168.         MOV     BX,AX
  169.         AND     AX,0FE00H
  170.         AND     BX,0FH
  171.         MOV     WORD PTR DS:[FCB+RR],AX         ;Position in file of program
  172.         MOV     WORD PTR DS:[FCB+RR+2],BX       ;Record size
  173.         MOV     DX,[PAGES]                      ;Size in 512 byte blocks
  174.         DEC     DX
  175.         XCHG    DH,DL
  176.         ROL     DX,1
  177.         MOV     DI,DX
  178.         MOV     SI,DX
  179.         AND     DI,0FE00H
  180.         AND     SI,1FFH
  181.         SUB     DI,AX
  182.         SBB     SI,BX
  183.         MOV     AX,[LASTP]
  184.         OR      AX,AX
  185.         JNZ     PARTP
  186.         MOV     AX,200H
  187. PARTP:
  188.         ADD     DI,AX
  189.         ADC     SI,0
  190.         MOV     AX,DI
  191.         ADD     AX,15
  192.         AND     AL,0F0H
  193.         OR      AX,SI
  194.         MOV     CL,4
  195.         ROR     AX,CL
  196.         XCHG    AX,CX
  197.         MOV     BX,[PROG_AREA]
  198.         ADD     BX,10H
  199.         MOV     AX,WORD PTR DS:[2]
  200.         SUB     AX,CX
  201.         MOV     DX,OFFSET DG:TOOBIG
  202.         JB      ERROR
  203.         CMP     BX,AX
  204.         JA      ERROR
  205.         CMP     [LOADLOW],-1
  206.         JNZ     LOADEXE
  207.         XCHG    AX,BX
  208. LOADEXE:
  209.         MOV     BP,AX
  210.         XOR     DX,DX
  211.         CALL    READ
  212.         JC      HAVEXE
  213. BADEXE:
  214.         MOV     DX,OFFSET DG:EXEBAD
  215.  
  216. ERROR:
  217.         MOV     AH,PRINTBUF             ;Print the message in DX
  218.         INT     21H
  219.         INT     20H                     ;Exit
  220.  
  221. HAVEXE:
  222.         MOV     AX,[RELTAB]             ;Get position of relocation table
  223.         MOV     WORD PTR DS:[FCB+RR],AX
  224.         MOV     WORD PTR DS:[FCB+RR+2],0
  225.         MOV     DX,OFFSET DG:RELPT      ;Four byte buffer
  226.         MOV     AH,SETDMA
  227.         INT     21H
  228.         CMP     [RELCNT],0
  229.         JZ      NOREL
  230. RELOC:
  231.         MOV     AH,BLKRD
  232.         MOV     DX,FCB
  233.         MOV     CX,4
  234.         INT     21H             ;Read in one relocation pointer
  235.         OR      AL,AL
  236.         JNZ     BADEXE
  237.         MOV     DI,[RELPT]      ;Pointer offset
  238.         MOV     AX,[RELSEG]     ;pointer segment
  239.         ADD     AX,BP           ;Bias with actual load segment
  240.         MOV     ES,AX
  241.         ADD     ES:[DI],BP      ;Relocate
  242.         DEC     [RELCNT]
  243.         JNZ     RELOC
  244.  
  245. NOREL:
  246.         ADD     [INITSS],BP
  247.         ADD     [INITCS],BP
  248.         JMP     SHORT PROGGO
  249.  
  250. BINFIL:
  251.         MOV     WORD PTR DS:[FCB+RECLEN],1
  252.         MOV     SI,-1
  253.         MOV     DI,SI
  254.         CALL    READ
  255.         MOV     ES,[PROG_SA]            ;Prog segment to ES
  256.         MOV     AX,WORD PTR ES:[6]
  257.         MOV     [PROG_SP],AX            ;Default SP for non EXE files
  258.         DEC     AH
  259.         MOV     WORD PTR ES:[6],AX      ;Fix size
  260.         
  261. PROGGO:
  262.         PUSH    DS
  263.         MOV     AX,[PROG_AREA]
  264.         MOV     DS,AX
  265.         MOV     DX,80H
  266.         MOV     AH,SETDMA
  267.         INT     21H                     ;Set default disk transfer address
  268.         POP     DS
  269.         MOV     BX,[BUCKET_NUM]
  270.         SHL     BX,1                    ;Mult by 2 to get #bytes in bucket area
  271. CLEAR:
  272.         MOV     BUCKET[BX],0            ;Zero counts
  273.         SUB     BX,2
  274.         JGE     CLEAR
  275.         MOV     DX,[CLOCK_GRAIN]
  276.         PUSH    DS
  277.         POP     ES
  278.         CLI                             ;Don't collect data yet
  279.         CALL    CLOCKON                 ;Set the interrupt
  280.         MOV     SI,[PROG_RA]
  281.         MOV     DI,[PROG_AREA]
  282.         MOV     BX,[PROG_SS]
  283.         MOV     CX,[PROG_SP]
  284.         MOV     AX,[DRV_VALID]
  285.         MOV     DX,[PROG_SA]
  286.         MOV     SS,BX
  287.         MOV     SP,CX
  288.         XOR     CX,CX
  289.         PUSH    CX                      ;0 on prog stack
  290.         PUSH    DX
  291.         PUSH    SI
  292.         MOV     DS,DI                   ;Set up segments
  293.         MOV     ES,DI
  294.         STI                             ;Start collecting data
  295. XXX     PROC    FAR
  296.         RET                             ;Hop to program
  297. XXX     ENDP
  298.         
  299. READ:
  300. ; AX:DX is disk transfer address (segment:offset)
  301. ; SI:DI is 32 bit length
  302.  
  303. RDLOOP:
  304.         MOV     BX,DX
  305.         AND     DX,000FH
  306.         MOV     CL,4
  307.         SHR     BX,CL
  308.         ADD     AX,BX
  309.         PUSH    AX
  310.         PUSH    DX
  311.         PUSH    DS
  312.         MOV     DS,AX
  313.         MOV     AH,SETDMA
  314.         INT     21H
  315.         POP     DS
  316.         MOV     DX,FCB
  317.         MOV     CX,0FFF0H               ;Keep request in segment
  318.         OR      SI,SI                   ;Need > 64K?
  319.         JNZ     BIGRD
  320.         MOV     CX,DI                   ;Limit to amount requested
  321. BIGRD:
  322.         MOV     AH,BLKRD
  323.         INT     21H
  324.         SUB     DI,CX                   ;Subtract off amount done
  325.         SBB     SI,0                    ;Ripple carry
  326.         CMP     AL,1                    ;EOF?
  327.         POP     DX
  328.         POP     AX                      ;Restore transfer address
  329.         JZ      RET10
  330.         ADD     DX,CX                   ;Bump transfer address by last read
  331.         MOV     BX,SI
  332.         OR      BX,DI                   ;Finished with request
  333.         JNZ     RDLOOP
  334. RET10:  STC
  335.         RET
  336.  
  337.  
  338. ;Return here on termination or abort
  339.  
  340. TERMINATE:
  341.         CLI                             ;Stop collecting data
  342.         MOV     DX,OFFSET DG:ENDMES
  343.         JMP     SHORT WRITEOUT
  344. ABORT:
  345.         CLI                             ;Stop collecting data
  346.         MOV     DX,OFFSET DG:ABORTMES
  347. WRITEOUT:
  348.         MOV     AX,CS
  349.         MOV     DS,AX
  350.         MOV     SS,AX
  351.         MOV     SP,OFFSET DG:STACK      ;Use internal stack
  352.         PUSH    DX
  353.         CALL    CLOCKOFF                ;Restore original clock routine
  354.         STI                             ;Back to normal clock
  355.         POP     DX
  356.         MOV     AH,PRINTBUF
  357.         INT     21H                     ;Apropriate termination message
  358.         MOV     [OUT_FCB+14],2          ;Word size records
  359.         MOV     DX,OFFSET DG:OUTPUT_DATA
  360.         MOV     AH,SETDMA
  361.         INT     21H                     ;Set the transfer address
  362.         MOV     CX,NUM_DATA_WORDS
  363.         ADD     CX,[BUCKET_NUM]
  364.         MOV     DX,OFFSET DG:OUT_FCB
  365.         MOV     AH,BLKWRT
  366.         INT     21H                     ;Write out data
  367.         MOV     DX,OFFSET DG:OUT_FCB
  368.         MOV     AH,CLOSE
  369.         INT     21H
  370.         INT     20H                     ;Exit
  371.  
  372.  
  373. ;The clock interrupt routine
  374. CLK_INTER       PROC    NEAR
  375.         CLI
  376.         PUSH    DS
  377.         PUSH    CS
  378.         POP     DS                      ;Get profile segment
  379.         MOV     [AXSAVE],AX
  380.         MOV     [BXSAVE],BX
  381.         POP     AX                      ;old DS
  382.         MOV     BX,OFFSET DG:LEAVE_INT
  383.         PUSH    BX
  384.         PUSH    AX
  385.         PUSH    ES
  386.         PUSH    [AXSAVE]
  387.         PUSH    [BXSAVE]
  388.         PUSH    CX
  389.         PUSH    DX
  390.  
  391.  
  392. ;Stack looks like this
  393. ;
  394. ; +18   OLDFLAGS
  395. ; +16   OLDCS
  396. ; +14   OLDIP
  397. ; +12   RETURN TO LEAVE_INT
  398. ; +10   OLDDS
  399. ; +8    OLDES
  400. ; +6    OLDAX
  401. ; +4    OLDBX
  402. ; +2    OLDCX
  403. ;SP->   OLDDX
  404.  
  405.         MOV     BX,SP
  406.         LES     BX,DWORD PTR SS:[BX+14]         ;Get CS:IP
  407.         MOV     AX,BX
  408.         MOV     CL,4
  409.         SHR     AX,CL
  410.         MOV     CX,ES
  411.         ADD     AX,CX                   ;Paragraph of CS:IP
  412.         CMP     AX,[DOS_PA]             ;Below DOS?
  413.         JB      IOHIT
  414.         CMP     AX,[PROG_LOW_PA]        ;Below program?
  415.         JB      DOSHIT
  416.         CMP     AX,[PROG_HIGH_PA]       ;Above program?
  417.         JAE     MISSH
  418.  
  419.         SUB     AX,[PROG_LOW_PA]        ;Paragraph offset
  420.         XOR     DX,DX
  421.         
  422.         DIV     [BUCKET_SIZE]
  423.         MOV     BX,AX
  424.         SHL     BX,1                    ;Mult by 2 to get byte offset
  425.         INC     BUCKET[BX]
  426.         JMP     SHORT DONE
  427.  
  428. IOHIT:
  429.         INC     [HIT_IO]
  430.         JMP     SHORT DONE
  431.  
  432. DOSHIT:
  433.         INC     [HIT_DOS]
  434.         JMP     SHORT DONE
  435.  
  436. MISSH:
  437.         INC     [HIT_HIGH]
  438.  
  439. DONE:
  440.         POP     DX
  441.         POP     CX
  442.         POP     BX
  443.         POP     AX
  444.         POP     ES
  445.         POP     DS
  446.         STI
  447.         RET             ;To LEAVE_INT
  448.  
  449. CLK_INTER       ENDP
  450.  
  451. CODE    ENDS
  452.  
  453. ;The init segment contains code to process input parameters
  454. ; It will be blasted as soon as the program to be run is read in
  455. ; And/or the bucket area is initialized
  456.  
  457. INIT    SEGMENT BYTE
  458.         ORG     0
  459.  
  460. SETUP:
  461.         MOV     DX,FCB
  462.         MOV     AH,OPEN
  463.         INT     21H                     ;Open program file
  464.         AND     AL,AL
  465.         JZ      OPENOK
  466.         MOV     DX,OFFSET DG:NOFILE
  467.         JMP     ERROR
  468.  
  469. OPENOK:
  470.         XOR     BX,BX
  471.         MOV     WORD PTR DS:[FCB+RR],BX
  472.         MOV     WORD PTR DS:[FCB+RR+2],BX       ;RR to 0
  473.         MOV     SI,FCB
  474.         MOV     DI,OFFSET DG:OUT_FCB
  475.         MOV     CX,4
  476.         REP     MOVSW
  477.         MOVSB                           ;Transfer drive spec and file to output
  478.         MOV     DX,OFFSET DG:OUT_FCB
  479.         MOV     AH,CREATE
  480.         INT     21H                     ;Try to create the output file
  481.         AND     AL,AL
  482.         JZ      GETSIZE
  483.         MOV     DX,OFFSET DG:OUTERR
  484.         JMP     ERROR
  485.  
  486. GETSIZE:                                ;Get bucket size
  487.         MOV     DX,OFFSET DG:SIZE_PROMPT
  488.         MOV     AH,PRINTBUF
  489.         INT     21H
  490.         CALL    INBUF
  491.         CALL    SCANB
  492.         JZ      GETSIZE         ;SCANB went to CR
  493.         XOR     BX,BX
  494.         INC     BX              ;Size >=1
  495.         CALL    GETNUM
  496.         JC      GETSIZE         ;Bad number
  497.         MOV     [BUCKET_SIZE],DX
  498.  
  499.         CMP     WORD PTR DS:[FCB+9],5800H+"E"           ;"EX"
  500.         JNZ     NOTEXE
  501.         CMP     BYTE PTR DS:[FCB+11],"E"
  502.         JNZ     NOTEXE
  503.  
  504. LOADEXEHEAD:                            ;Load the EXE header
  505.         MOV     [EXEFILE],1
  506.         MOV     DX,OFFSET DG:RUNVAR     ;Read header in here
  507.         MOV     AH,SETDMA
  508.         INT     21H
  509.         MOV     CX,RUNVARSIZ
  510.         MOV     DX,FCB
  511.         MOV     WORD PTR DS:[FCB+RECLEN],1
  512.         OR      AL,AL
  513.         MOV     AH,BLKRD
  514.         INT     21H
  515.         CMP     [RELPT],5A4DH           ;Magic number
  516.         JZ      EXEOK
  517.         JMP     BADEXE
  518. EXEOK:
  519.         MOV     AX,[PAGES]              ;Size of file in 512 byte blocks
  520.         MOV     CL,5
  521.         SHL     AX,CL                   ;Size in paragraphs     
  522.         JMP     SHORT SETBUCKET
  523.  
  524. NOTEXE:
  525.         MOV     AX,WORD PTR DS:[FCB+FILELEN]
  526.         MOV     DX,WORD PTR DS:[FCB+FILELEN+2]  ;Size of file in bytes DX:AX
  527.         ADD     AX,15
  528.         ADC     DX,0                            ;Round to PARA
  529.         MOV     CL,4
  530.         SHR     AX,CL
  531.         AND     AX,0FFFH
  532.         MOV     CL,12
  533.         SHL     DX,CL
  534.         AND     DX,0F000H
  535.         OR      AX,DX                           ;Size in paragraphs to AX
  536.         MOV     [PROG_RA],100H                  ;Default offset
  537.  
  538. SETBUCKET:
  539.         PUSH    AX                      ;Save size
  540.         XOR     DX,DX
  541.         DIV     [BUCKET_SIZE]
  542.         INC     AX                      ;Round up
  543.         MOV     [BUCKET_NUM],AX
  544.         MOV     BX,OFFSET DG:BUCKET
  545.         SHL     AX,1                    ;Number of bytes in bucket area
  546.         ADD     AX,BX                   ;Size of profil in bytes
  547.         ADD     AX,15                   ;Round up to PARA boundry
  548.         MOV     CL,4
  549.         SHR     AX,CL                   ;Number of paragraphs in profil
  550.         INC     AX                      ;Insurance
  551.         MOV     BX,CS
  552.         ADD     AX,BX
  553.         MOV     [PROG_AREA],AX
  554.  
  555.         CMP     [EXEFILE],1
  556.         JZ      SETBOUNDS
  557.         MOV     AX,[PROG_AREA]          ;Set up .COM segments
  558.         MOV     [PROG_SS],AX
  559.         MOV     [PROG_SA],AX
  560.  
  561. SETBOUNDS:                              ;Set the sample window
  562.         MOV     BX,10H                  ;Get start offset
  563.         ADD     BX,[PROG_AREA]          ;PARA # of start
  564.         MOV     [PROG_LOW_PA],BX
  565.         POP     AX                      ;Recall size of PROG in paragraphs
  566.         ADD     BX,AX
  567.         MOV     [PROG_HIGH_PA],BX
  568.  
  569. SETDOS:
  570.         XOR     DX,DX
  571.         MOV     ES,DX                   ;look in interrupt area
  572.         MOV     DX,WORD PTR ES:[82H]    ;From int #20
  573.         MOV     [DOS_PA],DX
  574.         PUSH    DS
  575.         POP     ES
  576.  
  577. GETGRAIN:                               ;Get sample interval
  578.         MOV     DX,OFFSET DG:GRAIN_PROMPT
  579.         MOV     AH,PRINTBUF
  580.         INT     21H
  581.         CALL    INBUF
  582.         CALL    SCANB
  583.         JZ      GETGRAIN                ;SCANB went to CR
  584.         MOV     BX,60                   ;Grain >=60
  585.         CALL    GETNUM
  586.         JC      GETGRAIN                ;Bad number
  587.         MOV     [CLOCK_GRAIN],DX
  588.  
  589.         MOV     DX,OFFSET DG:PARAM_PROMPT
  590.         MOV     AH,PRINTBUF
  591.         INT     21H
  592.         CALL    INBUF                   ;Get program parameters
  593.  
  594.         MOV     AX,2522H                ;Set vector 22H
  595.         MOV     DX,OFFSET DG:TERMINATE
  596.         INT     21H
  597.         MOV     AL,23H                  ;Set vector 23H
  598.         MOV     DX,OFFSET DG:ABORT
  599.         INT     21H
  600.         RET                             ;Back to resident code
  601.  
  602. GETNUM:                         ;Get a number, DS:SI points to buffer, carry set if bad
  603.         XOR     DX,DX
  604.         MOV     CL,0
  605.         LODSB
  606. NUMLP:
  607.         SUB     AL,"0"
  608.         JB      NUMCHK
  609.         CMP     AL,9
  610.         JA      NUMCHK
  611.         CMP     DX,6553
  612.         JAE     BADNUM
  613.         MOV     CL,1
  614.         PUSH    BX
  615.         MOV     BX,DX
  616.         SHL     DX,1
  617.         SHL     DX,1
  618.         ADD     DX,BX
  619.         SHL     DX,1
  620.         CBW
  621.         POP     BX
  622.         ADD     DX,AX
  623.         LODSB
  624.         JMP     NUMLP
  625. NUMCHK:
  626.         CMP     CL,0
  627.         JZ      BADNUM
  628.         CMP     BX,DX
  629.         JA      BADNUM
  630.         CLC
  631.         RET
  632. BADNUM:
  633.         STC
  634.         RET     
  635.  
  636. INBUF:                                  ;Read in from console, SI points to start on exit
  637.         MOV     AH,GETBUF
  638.         MOV     DX,OFFSET DG:LINEBUF
  639.         INT     21H
  640.         MOV     SI,2 + OFFSET DG:LINEBUF
  641.         MOV     DI,OFFSET DG:BYTEBUF
  642. CASECHK:
  643.         LODSB
  644.         CMP     AL,'a'
  645.         JB      NOCONV
  646.         CMP     AL,'z'
  647.         JA      NOCONV
  648.         ADD     AL,"A"-"a"              ;Convert to upper case
  649. NOCONV:
  650.         STOSB
  651.         CMP     AL,13
  652.         JZ      INDONE
  653.         CMP     AL,'"'
  654.         JNZ     QUOTSCAN
  655.         CMP     AL,"'"
  656.         JNZ     CASECHK
  657. QUOTSCAN:
  658.         MOV     AH,AL
  659. KILLSTR:
  660.         LODSB
  661.         STOSB
  662.         CMP     AL,13
  663.         JZ      INDONE
  664.         CMP     AL,AH
  665.         JNZ     KILLSTR
  666.         JMP     SHORT CASECHK
  667.  
  668. INDONE:
  669.         MOV     SI,OFFSET DG:BYTEBUF
  670.  
  671. ;Output CR/LF
  672.  
  673. CRLF:
  674.         MOV     AL,13
  675.         CALL    OUT
  676.         MOV     AL,10
  677.  
  678. OUT:
  679.         PUSH    AX
  680.         PUSH    DX
  681.         AND     AL,7FH
  682.         XCHG    AX,DX
  683.         MOV     AH,OUTCH
  684.         INT     21H
  685.         POP     DX
  686.         POP     AX
  687.         RET
  688.  
  689. SCANB:                          ;Scan to first non-blank
  690.         PUSH    AX
  691. SCANNEXT:
  692.         LODSB
  693.         CMP     AL," "
  694.         JZ      SCANNEXT
  695.         CMP     AL,9
  696.         JZ      SCANNEXT
  697.         DEC     SI
  698.         POP     AX
  699. EOLCHK:
  700.         CMP     BYTE PTR[SI],13
  701.         RET
  702.  
  703. INIT    ENDS
  704.         END     START
  705.